package ar.edu.uba.fi.jmit.aspecter;

import java.io.File;
import java.lang.reflect.InvocationTargetException;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.Loader;
import ar.edu.uba.fi.jmit.aspecter.core.WeavingInstrumentor;
import ar.edu.uba.fi.jmit.aspecter.core.WeavingInstrumentorImpl;
import ar.edu.uba.fi.jmit.aspecter.core.exception.AspecterException;
import ar.edu.uba.fi.jmit.aspecter.core.injector.Injector;
import ar.edu.uba.fi.jmit.aspecter.core.interceptor.Interceptor;
import ar.edu.uba.fi.jmit.aspecter.testUtils.DebugAdviceWeaver;
import ar.edu.uba.fi.jmit.aspecter.testUtils.ExceptionAfterMethodInterceptor;
import ar.edu.uba.fi.jmit.aspecter.testUtils.ExceptionAfterNewInstanceInterceptor;
import ar.edu.uba.fi.jmit.aspecter.testUtils.ExceptionBeforeConstructorInterceptor;
import ar.edu.uba.fi.jmit.aspecter.testUtils.ExceptionConstructorInjector;
import ar.edu.uba.fi.jmit.aspecter.testUtils.ExceptionInjection;

public class CoreTestCase extends AspecterTestCase {

	public void testOneInjector() throws InterruptedException {
		DebugAdviceWeaver weaver = new DebugAdviceWeaver("target/test-classes", "target/test-backups");
		WeavingInstrumentor instrumentor = new WeavingInstrumentorImpl();
		Injector injector = new ExceptionInjection("getX", int.class);
		instrumentor.addInjector("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest1", injector);
		weaver.addInstrumentor(instrumentor);
		try {
			weaver.applyAdvice(new File("target/test-classes/ar"));
		} catch (AspecterException e) {
			fail(e);
		}
		try {
			CtClass ctClazz = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest1", getNextClassName());
			Class<?> clazz = ctClazz.toClass();
			Object point = clazz.newInstance();
			clazz.getMethod("getY").invoke(point);
			try {
				clazz.getMethod("getX").invoke(point);
				fail("Exception should be thrown!");
			}catch(InvocationTargetException e) {
				//here should be wrapped the runtime exception
				assertEquals(RuntimeException.class, e.getTargetException().getClass());
				assertEquals("injected!", e.getTargetException().getMessage());
			}
//			for(int i = 2; i < 10; i++) {
//				ctClazz = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest" + i, getNextClassName());
//				clazz = ctClazz.toClass();
//				point = clazz.newInstance();
//				clazz.getMethod("getY").invoke(point);
//				clazz.getMethod("getX").invoke(point);
//			}
		} catch (Exception e) {
			fail(e);
		}finally {
			weaver.restore();
		}
	}
	
	public void testMultipleInjectors() {
		Loader loader = new Loader();
		Thread.currentThread().setContextClassLoader(loader);
		DebugAdviceWeaver weaver = new DebugAdviceWeaver("target/test-classes", "target/test-backups");
		WeavingInstrumentor instrumentor = new WeavingInstrumentorImpl();
		Injector injector = new ExceptionInjection("getX", int.class);
		instrumentor.addInjector("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest2", injector);
		Injector injector2 = new ExceptionInjection("getX", int.class);
		instrumentor.addInjector("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest3", injector2);
		Injector injector3 = new ExceptionInjection("getX", int.class);
		instrumentor.addInjector("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest4", injector3);
		weaver.addInstrumentor(instrumentor);
		try {
			weaver.applyAdvice(new File("target/test-classes/ar"));
		} catch (AspecterException e) {
			fail(e);
		}
		try {
			CtClass ctClazz = null;
			Class<?> clazz = null;
			Object point = null;
			//aspected
			for(int i = 2; i <=4; i++) {
				ctClazz = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest" + i, getNextClassName());
				clazz = ctClazz.toClass();
				point = clazz.newInstance();
				clazz.getMethod("getY").invoke(point);
				try {
					clazz.getMethod("getX").invoke(point);
					fail("Exception should be thrown!");
				}catch(InvocationTargetException e) {
					//here should be wrapped the runtime exception
					assertEquals(RuntimeException.class, e.getTargetException().getClass());
					assertEquals("injected!", e.getTargetException().getMessage());
				}
			}
			//Not aspected
//			for(int i = 5; i < 10; i++) {
//				ctClazz = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest" + i, getNextClassName());
//				clazz = ctClazz.toClass();
//				point = clazz.newInstance();
//				clazz.getMethod("getY").invoke(point);
//				clazz.getMethod("getX").invoke(point);
//			}
		} catch (Exception e) {
			fail(e);
		}finally {
			weaver.restore();
			Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
		}
	}
	
	public void testOneInterceptor() throws ClassNotFoundException {
		DebugAdviceWeaver weaver = new DebugAdviceWeaver("target/test-classes", "target/test-backups");
		WeavingInstrumentor instrumentor = new WeavingInstrumentorImpl();
		Interceptor interceptor = new ExceptionAfterMethodInterceptor("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest4", "getY");
		instrumentor.addInterceptor(interceptor);
		weaver.addInstrumentor(instrumentor);
		try {
			weaver.applyAdvice(new File("target/test-classes/ar"));
		} catch (AspecterException e) {
			fail(e);
		}
		try {
			CtClass ctClazz = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest3", getNextClassName());
			Class<?> clazz = ctClazz.toClass();
			Object point = clazz.newInstance();
			try {
				clazz.getMethod("getY").invoke(point);
				fail("Exception should be thrown!");
			}catch(InvocationTargetException e) {
				//here should be wrapped the runtime exception
				assertEquals(RuntimeException.class, e.getTargetException().getClass());
				assertEquals("intercepted!", e.getTargetException().getMessage());
			}
		} catch (Exception e) {
			fail(e);
		}finally {
			weaver.restore();
		}
	}
	
	public void testAll() {
		DebugAdviceWeaver weaver = new DebugAdviceWeaver("target/test-classes", "target/test-backups");
		try {
			WeavingInstrumentor instrumentor = new WeavingInstrumentorImpl();
			Interceptor interceptorConstructor = new ExceptionBeforeConstructorInterceptor("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest8");
			instrumentor.addInterceptor(interceptorConstructor);
			Interceptor interceptorNewInstance = new ExceptionAfterNewInstanceInterceptor("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest6");
			instrumentor.addInterceptor(interceptorNewInstance);
			Injector constructorInjector = new ExceptionConstructorInjector();
			instrumentor.addInjector("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest10", constructorInjector);
			weaver.addInstrumentor(instrumentor);
			weaver.applyAdvice(new File("target/test-classes/ar"));
		} catch (Exception e) {
			fail(e);
		}
		try {
			CtClass ctClazz = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest9", getNextClassName());
			Class<?> clazz = ctClazz.toClass();
			try {
				clazz.newInstance();
				fail("Exception should be thrown!");
			}catch(RuntimeException e) {
				assertEquals("intercepted!", e.getMessage());
			}
			
			ctClazz = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest5", getNextClassName());
			clazz = ctClazz.toClass();
			try {
				clazz.newInstance();
				fail("Exception should be thrown!");
			}catch(RuntimeException e) {
				assertEquals("intercepted!", e.getMessage());
			}
			
			ctClazz = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.coreTest.PointTest10", getNextClassName());
			clazz = ctClazz.toClass();
			try {
				clazz.newInstance();
				fail("Exception should be thrown!");
			}catch(RuntimeException e) {
				assertEquals("injected!", e.getMessage());
			}
		} catch (Exception e) {
			fail(e);
		}finally {
			weaver.restore();
		}
	}
	
}
